Skip to content

fix: use sync.Once around create_comment API call#11

Open
zakisk wants to merge 2 commits into
mainfrom
fix-duplicate-comments-in-e2e
Open

fix: use sync.Once around create_comment API call#11
zakisk wants to merge 2 commits into
mainfrom
fix-duplicate-comments-in-e2e

Conversation

@zakisk

@zakisk zakisk commented Feb 14, 2026

Copy link
Copy Markdown
Owner

this commit uses sync.Once to make sure that create_comment is called only once. it stores the sync.Once instance for every unique PR in sync.Map and retrieves and call the API in it.

📝 Description of the Change

👨🏻‍ Linked Jira

🔗 Linked GitHub Issue

Fixes #

🚀 Type of Change

  • 🐛 Bug fix (fix:)
  • ✨ New feature (feat:)
  • 💥 Breaking change (feat!:, fix!:)
  • 📚 Documentation update (docs:)
  • ⚙️ Chore (chore:)
  • 💅 Refactor (refactor:)
  • 🔧 Enhancement (enhance:)
  • 📦 Dependency update (deps:)

🧪 Testing Strategy

  • Unit tests
  • Integration tests
  • End-to-end tests
  • Manual testing
  • Not Applicable

🤖 AI Assistance

  • I have not used any AI assistance for this PR.
  • I have used AI assistance for this PR.

If you have used AI assistance, please provide the following details:

Which LLM was used?

  • GitHub Copilot
  • ChatGPT (OpenAI)
  • Claude (Anthropic)
  • Cursor
  • Gemini (Google)
  • Other: ____________

Extent of AI Assistance:

  • Documentation and research only
  • Unit tests or E2E tests only
  • Code generation (parts of the code)
  • Full code generation (most of the PR)
  • PR description and comments
  • Commit message(s)

Important

If the majority of the code in this PR was generated by an AI, please add a Co-authored-by trailer to your commit message.
For example:

Co-authored-by: Gemini gemini@google.com
Co-authored-by: ChatGPT noreply@chatgpt.com
Co-authored-by: Claude noreply@anthropic.com
Co-authored-by: Cursor noreply@cursor.com
Co-authored-by: Copilot Copilot@users.noreply.github.com

**💡You can use the script ./hack/add-llm-coauthor.sh to automatically add
these co-author trailers to your commits.

✅ Submitter Checklist

  • 📝 My commit messages are clear, informative, and follow the project's How to write a git commit message guide. The Gitlint linter ensures in CI it's properly validated
  • ✨ I have ensured my commit message prefix (e.g., fix:, feat:) matches the "Type of Change" I selected above.
  • ♽ I have run make test and make lint locally to check for and fix any
    issues. For an efficient workflow, I have considered installing
    pre-commit and running pre-commit install to
    automate these checks.
  • 📖 I have added or updated documentation for any user-facing changes.
  • 🧪 I have added sufficient unit tests for my code changes.
  • 🎁 I have added end-to-end tests where feasible. See README for more details.
  • 🔎 I have addressed any CI test flakiness or provided a clear reason to bypass it.
  • If adding a provider feature, I have filled in the following and updated the provider documentation:
    • GitHub App
    • GitHub Webhook
    • Gitea/Forgejo
    • GitLab
    • Bitbucket Cloud
    • Bitbucket Data Center

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @zakisk, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves an issue where multiple identical comments could be created on GitHub pull requests due to concurrent processing. By integrating Go's sync.Once primitive, the system now guarantees that the API call to create a comment is executed only a single time for any given pull request, thereby eliminating redundant comments and enhancing the stability of the comment posting mechanism.

Highlights

  • Duplicate Comment Prevention: Addressed a race condition that led to duplicate comments being posted on GitHub pull requests.
  • Concurrency Control: Implemented sync.Once to ensure the create_comment API call is executed exactly once per pull request.
  • Workaround Removal: Replaced a previous, less reliable workaround involving random delays and re-checks with a robust sync.Once mechanism.
  • State Management: Utilized sync.Map to store and retrieve sync.Once instances, keyed by pull request identifiers, ensuring proper isolation for each PR.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • pkg/provider/github/github.go
    • Removed the math/rand import, as the random sleep workaround was deprecated.
    • Introduced a global sync.Map to manage sync.Once instances for each unique pull request.
    • Refactored the CreateComment function to wrap the GitHub API call within a sync.Once.Do() block, ensuring single execution.
    • Eliminated the previous workaround logic that involved a random jitter and re-checking for existing comments before creating a new one.
Activity
  • The pull request author, zakisk, created this PR to fix a known issue with duplicate comments.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request aims to replace a fragile, sleep-based workaround for a race condition with a more robust sync.Once mechanism to prevent duplicate comment creation on GitHub Pull Requests. However, the current implementation introduces significant security and functional flaws. It causes a permanent memory leak due to the global sync.Map lacking an eviction strategy, which can lead to resource exhaustion. Furthermore, it incorrectly suppresses all subsequent comments on a PR after the first one is attempted for the lifetime of the process, potentially hiding critical status or security notifications, and leads to false positive success reports in concurrent scenarios. Additionally, the condition for using the synchronization logic is too restrictive, leaving some pull request-related events unprotected from the race condition.

Comment on lines +809 to 830
var once *sync.Once
var err error
if event.TriggerTarget == triggertype.PullRequest {
key := fmt.Sprintf("%s/%s/%d", event.Organization, event.Repository, event.PullRequestNumber)
value, _ := syncMap.LoadOrStore(key, &sync.Once{})
var ok bool
once, ok = value.(*sync.Once)
if !ok {
return fmt.Errorf("unexpected type in sync map for key %s", key)
}
} else {
once = &sync.Once{}
}

_, _, err := wrapAPI(v, "create_comment", func() (*github.IssueComment, *github.Response, error) {
return v.Client().Issues.CreateComment(ctx, event.Organization, event.Repository, event.PullRequestNumber, &github.IssueComment{
Body: github.Ptr(commit),
once.Do(func() {
_, _, err = wrapAPI(v, "create_comment", func() (*github.IssueComment, *github.Response, error) {
return v.Client().Issues.CreateComment(ctx, event.Organization, event.Repository, event.PullRequestNumber, &github.IssueComment{
Body: github.Ptr(commit),
})
})
})
return err

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The synchronization logic using sync.Once per Pull Request is fundamentally flawed. It leads to permanent suppression of comments because sync.Once instances are never cleared from the global map, causing CreateComment to execute only once per PR for the controller's lifetime. This silently skips critical status updates and security notifications. Additionally, it can result in false positive success reports: if multiple goroutines call CreateComment concurrently and the API call fails for the executing goroutine, others will return nil (success) because their local err variable remains uninitialized. Furthermore, the check if event.TriggerTarget == triggertype.PullRequest is too restrictive. Events like /retest or /ok-to-test comments, which also operate on pull requests, would use a transient sync.Once, failing to prevent the intended race condition. The logic should be simplified to always use the sync.Map for sync.Once instances, ensuring all concurrent comment creation attempts for the same PR are synchronized regardless of the specific trigger type.

var err error
	key := fmt.Sprintf("%s/%s/%d", event.Organization, event.Repository, event.PullRequestNumber)
	value, _ := syncMap.LoadOrStore(key, &sync.Once{})
	once, ok := value.(*sync.Once)
	if !ok {
		return fmt.Errorf("unexpected type in sync map for key %s", key)
	}


var _ provider.Interface = (*Provider)(nil)

var syncMap = sync.Map{}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The syncMap global variable, used to store sync.Once instances for each unique Pull Request, creates a permanent memory leak as entries are never removed. This leads to indefinite memory growth, resource exhaustion, and potential Denial of Service (OOM) in long-running processes. To mitigate this, consider implementing a cache with a time-to-live (TTL) or a size-based eviction policy (like LRU) for sync.Once objects. Alternatively, a cleanup mechanism could be added, such as removing entries when pull requests are closed or merged.

@zakisk zakisk force-pushed the fix-duplicate-comments-in-e2e branch from fd8ae01 to 5720faf Compare February 19, 2026 10:34
Signed-off-by: Zaki Shaikh <zashaikh@redhat.com>
@zakisk zakisk force-pushed the fix-duplicate-comments-in-e2e branch from 5720faf to 8056245 Compare February 19, 2026 11:29
this commit uses sync.Once to make sure that create_comment
is called only once. it stores the sync.Once instance
for every unique PR in sync.Map and retrieves and call
the API in it.

Signed-off-by: Zaki Shaikh <zashaikh@redhat.com>
@zakisk zakisk force-pushed the fix-duplicate-comments-in-e2e branch from 8056245 to ea008b5 Compare February 19, 2026 11:35
@zakisk zakisk force-pushed the main branch 2 times, most recently from decf4c1 to 0faad24 Compare March 30, 2026 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant